﻿<!DOCTYPE html>
<html>
<head>
	<meta charset="UTF-8">
	<title>Add and Remove Layout - jQuery EasyUI Demo</title>
	<link rel="stylesheet" type="text/css" href="http://www.ifelsefor.cc/easyui14/themes/default/easyui.css">
	<link rel="stylesheet" type="text/css" href="http://www.ifelsefor.cc/easyui14/themes/icon.css">
	<link rel="stylesheet" type="text/css" href="http://www.ifelsefor.cc/easyui14/demo/demo.css">
	<script type="text/javascript" src="http://www.ifelsefor.cc/easyui14/jquery.min.js"></script>
	<script type="text/javascript" src="http://www.ifelsefor.cc/easyui14/jquery.easyui.min.js"></script>
	
<!-- codemirror -->
<link rel="stylesheet" href="CodeMirror/lib/codemirror.css">
<script src="CodeMirror/lib/codemirror.js" type="text/javascript"></script>
<script src="CodeMirror/mode/xml/xml.js" type="text/javascript"></script>
<script src="CodeMirror/mode/javascript/javascript.js" type="text/javascript"></script>
<script src="CodeMirror/mode/css/css.js" type="text/javascript"></script>
<script src="CodeMirror/mode/htmlmixed/htmlmixed.js" type="text/javascript"></script>
<style type="text/css"> 
    .icon-import{
	background:url('editorIcons/import.gif') no-repeat center center;
     }
     [sid]{border: 1px solid #ccc;position:absolute;}
</style>
<style type="text/css"> 
    .CodeMirror
    {
        height:100%;width:100%;
    }
   .CodeMirror .CodeMirror-scroll
    {
        height:100%;width:100%;
    }
</style>
<script type="text/javascript">
    var codeEditor = null;
    var lastDesignText = "";
    function runCode() {
        var text = codeEditor.getValue();
        var winname = window.open('SHOW.html', "_blank", '');
        winname.document.open('text/html', 'replace');
        winname.document.write(text);
        winname.document.close();
    }
    function onSelect1(title,index){
	if(title=='design-mode'){
	   var codeEditorCurrText = codeEditor.getValue();
	   if (lastDesignText != codeEditorCurrText) {
		var main = getCode('main',codeEditorCurrText,'div');
		var mainInner = (main = main.replace(/<div [^>]*sid="main"[^>]*>/i,"")).substring(0,main.search(/<\/div>$/gi));
		$('[sid=main]').html(mainInner);
		$.parser.parse($('[sid=main]')[0]);
		$('#EditDiv [sid][sid!=main]').draggable({disabled:false,onBeforeDrag:onBeforeDrag,onStopDrag:onStopDrag});
		$('#EditDiv [sid][sid!=main]').resizable({onStartResize:onStartResize,onStopResize:onStopResize});
	   }
	}
	else if(title=='source'){
	    if(codeEditor){
	    	lastDesignText = codeEditor.getValue();
	    }
	}
    }
</script>

</head>
<body>
	<span style="font-size:16px;font-weight:5;">Easy Dragger</span> easydragger is very easy but powerful,core code is in the OnePage
	<div class="easyui-panel" style="padding:5px;">
		<a href="#" onclick="saveAsLocalHtml();" class="easyui-linkbutton" data-options="plain:true,iconCls:'icon-save'">Save</a>
		<!-- <a href="#" onclick="" class="easyui-linkbutton" data-options="plain:true,iconCls:'icon-import'">Import</a> -->
		<a href="#" onclick="runCode();" class="easyui-linkbutton" data-options="plain:false,iconCls:''">RUN the source</a>
		<b>[支持Delete键删除组件,直接编辑响应同步双向可控,支持源代码CTRL+Z] [经测试在谷歌浏览器(<span style="color:red;">chrome</span>)上运行速度最快，在IE8上切换tab则较慢]</b>
	</div>
	<div class="easyui-tabs easyui-resizable" data-options="tabWidth:112,onSelect:onSelect1" style="width:100%;height:800px;">
	
		<div title="source" style="padding:10px;">
			<textarea id="text" ></textarea>
		</div>
		
		<div id="design" title="design-mode" style="padding:5px;width:auto;height:auto;">
			<div id="cc" class="easyui-layout" style="width:auto;height:700px;">
				<div data-options="region:'north'" style="height:30px"></div>
				<div data-options="region:'south',split:true" style="height:50px;"></div>
				<div data-options="region:'east',split:true" title="East" style="width:100px;"></div>
				<div data-options="region:'west',split:true" title="West" style="width:150px;padding-left:2px;">
					<div style="position:relative;width:115px;height:900px; border:1px solid #ccc;top:20px;">
					<div class="dragitem" component_id="Panel" style="border:1px solid #ccc;width:110px;height:40px;">Panel(可拖拽)</div>
					<div style="height: 5px;"></div>
					<div class="dragitem" component_id="tree" style="border:1px solid #ccc;width:110px;height:40px;">Tree(可拖拽)</div>
					<div style="height: 5px;"></div>
					<div class="dragitem" component_id="datagrid" style="border:1px solid #ccc;width:110px;height:40px;">DataGrid(可拖拽)</div>
					<div style="height: 5px;"></div>
					<div class="dragitem" component_id="datebox" style="border:1px solid #ccc;width:110px;height:40px;">datebox(可拖拽)</div>
					<div style="height: 5px;"></div>
					<div class="dragitem" component_id="combobox" style="border:1px solid #ccc;width:110px;height:40px;">combobox(可拖拽)</div>
					<div style="height: 5px;"></div>
					<div class="dragitem" component_id="button" style="border:1px solid #ccc;width:110px;height:40px;">按钮(可拖拽)</div>
					<div style="height: 5px;"></div>
					<div class="dragitem" component_id="textbox" style="border:1px solid #ccc;width:110px;height:40px;">输入框(可拖拽)</div>						
					<!-- 这里添加 -->
					<div style="height: 5px;"></div>
					<div class="dragitem" component_id="aaaa" style="border:1px solid #ccc;width:110px;height:40px;">aaa(可拖拽)</div>
					<!-- 这里添加 -->
					<div style="height: 5px;"></div>
					<div class="dragitem" component_id="bbbb" style="border:1px solid #ccc;width:110px;height:40px;">图片(可拖拽)</div>
					
				</div>
				</div>
				<div id="EditDiv" sid="main" style="position:relative;border:1px solid #ccc;" data-options="region:'center',title:'EditDiv'">
				</div>
			</div>
		</div>
	</div>
	
	<!-- 1、拖拽器 -->
	<script type="text/javascript">
		var proxy = null; // 拖拽代理
		var component_id = ''; // 从组件选择区拖拽出组件名
		var currComponent = null; // 当前拖拽的对象
		var lastleft = '',lasttop = '';
		var currentActionMap = {"Resize":false,"Drag":false}; // 当前事件名称“Resize”调整大小中，“Drag”拖动中
		$(function(){
			$(document).keydown(function(e){
				if(e.keyCode==46&&currComponent){
					// cache4
					editorCachePush();
					codeEditor.setValue( removeCode($(currComponent).attr("sid"),codeEditor.getValue()) );//删除代码
					$(currComponent).remove();//删除组件
				}
			});
			$("#design").keydown(function(e){
				if(e.keyCode==90&&e.ctrlKey){
					editorCachePop();
				}
			});
			// 初始化编辑区不能用.text,必须.val
			$("#text").val($.ajax({url:"frame.html",async:false}).responseText);	
			codeEditor = CodeMirror.fromTextArea($("#text")[0], { mode: "text/html", tabMode: "indent", height: "100%", width: "100%" });
			// 初始化可拖拽代理区
			$('.dragitem').draggable({
				 revert:true,
				 deltaX:2,
				 deltaY:2,
				 proxy:function(source){
					 var n = $('<div style="position:absolute;border:solid 1px #ccc;"></div>');
					// var com = n.html("sssss").appendTo('body');
					// $.parser.parse(com[0]);
					 component_id = $(source).attr('component_id');
					 var com = n.html($.ajax({url:component_id+".html",async:false}).responseText).appendTo('body');
					 $.parser.parse(com[0]);
					 return proxy=n;
				 },
				 onStopDrag:function(e){
					// 当代理拖拽停止时,将拖动的组件添加到编辑区
					var DragScopes = $('#EditDiv [sid]').add($('#EditDiv'));
					var parentdiv = findParentInScope(proxy[0],DragScopes);
					if(parentdiv){
						var text = $.ajax({url:component_id+".html",async:false}).responseText;
						var newsid = getSidCode();
						text = text.replace(/sid=["'].*?["']/, "sid=\""+newsid+"\"");
						var v = $(text);
						v.appendTo($(parentdiv)); 
						$.parser.parse(v[0]);
						v[0].style.position = 'absolute';
						v[0].style.border = 'solid 1px #ccc';
						v[0].style.left = ($(proxy[0]).offset().left -  $(parentdiv).offset().left-1) + "px";
						v[0].style.top = ($(proxy[0]).offset().top -  $(parentdiv).offset().top-1) + "px";
						//cache1
						editorCachePush();
						try{
							codeEditor.setValue( addCodeTo(text,$(parentdiv).attr('sid'),codeEditor.getValue()) );
							codeEditor.setValue( upPosCode($(v[0]).attr('sid'),v[0].style.left,v[0].style.top,codeEditor.getValue()) );
						}catch(e){alert('prox upPosCode:'+e)}
						v.draggable({disabled:false,onBeforeDrag:onBeforeDrag,onStopDrag:onStopDrag});
						v.resizable({onStartResize:onStartResize,onStopResize:onStopResize});
					}
				 }
			 });
		});
		
	
		/**
		 * 开始拖拽组件边框变红,变层为最高层目的是不被其他组件遮挡住
		 */
		function onBeforeDrag(e){
			currentActionMap.Drag=true;
			var d = e.data;
			$(d.target).css("z-index","9999");
			if(currComponent){currComponent.style.borderColor="#ccc";}
			currComponent = d.target;
			lastleft = currComponent.style.left; 
			lasttop = currComponent.style.top;
			d.target.style.borderColor="red";d.target.style.borderWidth="1px";
			$(d.target).parents("[sid][sid!=main]").draggable({disabled:true,onBeforeDrag:onBeforeDrag,onStopDrag:onStopDrag});
			$(d.target).parents("[sid][sid!=main]").resizable({disabled:true});
		}
		/**
		 *  当停止拖拽的时候,判断是否拖拽组件或容器以及逃离了父容器的边界，
		 *	如果脱离，则选择到达的最小容器作为其新父容器
		 */
		function onStopDrag(e){
			var d = e.data;
			$(d.target).css("z-index","0");
			// d.target.style.borderColor="#ccc";
			$(d.target).parents("[sid][sid!=main]").draggable({disabled:false,onBeforeDrag:onBeforeDrag,onStopDrag:onStopDrag});
			$(d.target).parents("[sid][sid!=main]").resizable({disabled:false});
			currentActionMap.Drag=false;
			if(currentActionMap.Resize){
				// 当Resize也在进行的时候明显，Resize的优先级高于Drag，调整大小的时候不应该让Drag发生，所以对于这种双事件相互干扰，直接返回
				return;
			}
			var DragScopes = $('#EditDiv [sid]').add($('#EditDiv'));
			var parentdiv = findParentInScope(d.target,DragScopes);
			if(parentdiv){
				// 父容器内发生了位置的改变更新组件位置
				if($(d.target).parent()[0]==parentdiv && (lastleft != d.target.style.left || lasttop != d.target.style.top)){
					// cache2
					editorCachePush();
					try{
						codeEditor.setValue( upPosCode($(d.target).attr('sid'),d.target.style.left,d.target.style.top, codeEditor.getValue() ) );
					}catch(e){alert('upPosCode:'+e)}
				}else if($(d.target).parent()[0]!=parentdiv){
					// 父容器归属改变更新组件位置
					// cache2
					editorCachePush();
					try{
						d.target.style.left = ($(d.target).offset().left -  $(parentdiv).offset().left - 1) + "px";
						d.target.style.top = ($(d.target).offset().top -  $(parentdiv).offset().top - 1) + "px";
						parentdiv.appendChild(d.target);
						codeEditor.setValue( upPosCode($(d.target).attr('sid'),d.target.style.left,d.target.style.top,codeEditor.getValue()) );
						codeEditor.setValue( moveCodeTo($(d.target).attr('sid'),$(parentdiv).attr('sid'),codeEditor.getValue()) );
					}catch(e){alert('upPosCode moveCodeTo:'+e)}
				}
				
			}	
		};
		
		/**
		 * 调整大小而已
		 */
		function onStartResize(e){
			currentActionMap.Resize=true;
		}
		function onStopResize(e){
			currentActionMap.Resize=false;
			var d = e.data;
			var width = d.target.style.width;
			var height = d.target.style.height;
			// cache3
			editorCachePush();
			$.parser.parse(d.target);
			try{
				codeEditor.setValue( upSizeCode($(d.target).attr('sid'),width,height,codeEditor.getValue()) );
			}catch(e){alert('upSizeCode:'+e)}
		}
		
		/** 
		  *	返回最小包含父容器方法
		  * 查看target元素落在了什么容器范围之内,如果多个容器范围都包含它则取小的那一个容器.
		  * @target 拖动元素
		  * @containers 用于寻找是否位置包含拖动元素target位置的容器集合
		  * @return 返回最小(宽度最小)包含父容器
		  */
		function findParentInScope(target,containers){
			var parentdiv = null;
			containers.each(function(){
				if(target!=this){
					if($(this).offset().left < $(target).offset().left)
					if($(this).offset().left + $(this).width() > $(target).offset().left + $(target).outerWidth())
					if($(this).offset().top < $(target).offset().top)
					if(($(this).offset().top + $(this).height()) > ($(target).offset().top + $(target).outerHeight()))
					if(parentdiv == null || $(this).width()<$(parentdiv).width()){
						parentdiv = this;
					}
			}});
			return parentdiv;
		}	
	
		function getSidCode(){
			var maxCode = 0;
			$('[sid][sid!=main]').each(function(){
				var sidNumber = Number($(this).attr('sid'));
				if( sidNumber > maxCode){
					maxCode = sidNumber;
				}
			});
			return maxCode+1+"";
		}
	</script>
	
	<!-- 2、代码修改器 -->
	<script>
			// element代码根据sid提取
			function getCode(sid,str,ele){
				var regexi = null;
				if(sid){
					regexi = new RegExp("<"+ele+"[^>]*sid=\""+sid+"\"[^>]*>","i");
				}else{
					regexi = new RegExp("<"+ele+"[^>]*>","i");
				}
				var sidindex = str.search(regexi);
				var sidStr = str.substring(sidindex);
				var regex3 = new RegExp("a?(<"+ele+"[^>]*\/>)|b?(<"+ele+"[^>]*>)|c?(</"+ele+">)","gi");	
				var arr = [],result = "";
				while ((result = regex3.exec(sidStr)) != null){
				  var group = (result[1]?"a":"")+(result[2]?"b":"")+(result[3]?"c":"");
				  arr.push({result:result[0],lastIndex:regex3.lastIndex,group:group});
				}
				if(arr[0].group=="a"){// sid是闭合标签直接返回
					return arr[0].result;
				}else if(arr[0].group=="b"){// sid不是闭合标签按平衡算法继续
					var numDivStart=0,numDivClose=0;// 开闭的量
					for(var i=1;i<arr.length;i++){
						if(arr[i].group=="b"){
							numDivStart = numDivStart+1; // 开 + 1
						}else if(arr[i].group=="c"){
							// 当闭合标签出现时，如果前面的闭合标签量平衡(numDivStart==numDivClose)，则返回以当前闭合标签结尾的字符串
							if(numDivStart==numDivClose){
								return sidStr.substring(0,arr[i].lastIndex);
							}
							numDivClose = numDivClose+1;// 闭 + 1
						}
					}
				}
				return null;
			}
			
			// 添加代码str1到sid中
			function addCodeTo(str1,sid,str){
				var sid1str = str1;
				var sid2str = getCode(sid,str,'div');
				var space = getSpace(sid2str,str);
				// 将拖拽的代码appendTo到sid中拼接成它的子元素
				var sid2newstr = sid2str.substring(0,sid2str.search(/<\/div>$/i));
				// 将拼接的新sid2代码更新
				sid2newstr = sid2newstr + sid1str + "</div>";
				str = str.replace(sid2str,sid2newstr);
				return str;
			}
			
			// 获取元素起始行的空白前缀字符
			function getSpace(sidstr,str){
				//元素sidstr前面的字符串 
				var prestr = str.substring(0,str.indexOf(sidstr));
				//结尾空白字符的起始位置
				var spaceStart = prestr.search(/\s+$/);
				if(spaceStart!=-1){ // 以空白字符结尾
					return space = prestr.substring(spaceStart).replace(/[^ 	]+/,"")
				}
				return "";
			}
			
			// 将当前拖动的sid1的元素添加到sid2元素中成为它(sid2)的子元素
			function moveCodeTo(sid1,sid2,str){
				var str1 = str;
				try{				
					// 删除sid1原来的代码
					var sid1str = getCode(sid1,str,'div');
					str1 = str1.replace(sid1str,"");
					// 将sid1的代码appendTo到sid2中拼接成它的子元素
					var sid2str = getCode(sid2,str1,'div');
					var sid2newstr = sid2str.substring(0,sid2str.search(/<\/div>$/i));
					// 将拼接的新sid2代码更新,并逐行退格以格式化
					sid2newstr = sid2newstr + sid1str + "</div>";
					str1 = str1.replace(sid2str,sid2newstr);
					return str1;
				}catch(e){alert(e);return str}
				return str1;
			}
			
			// 删除组件
			function removeCode(sid,str){
				var str1 = str;
				try{
					// 删除sid1的代码
					var sidstr = getCode(sid,str1,'div');
					str1 = str1.replace(sidstr,"")
				}catch(e){alert("removeCode:"+e);return str;};
				return str1;
			}
			
			// 更新元素绝对位置Style信息例position:absolute;left:99px;top:100px;
			function upPosCode(sid,left,top,str){
				var regex3 = new RegExp("<div [^>]*sid=\""+sid+"\"[^>]*>","gi");				
				var groups = str.match(regex3);
				var newgroup = groups[0];
				if(groups){
					//newgroup = newgroup.replace(/position\s*:[^"]*?;/i,"");
					newgroup = newgroup.replace(/left\s*:[^"]*?;/i,"");
					newgroup = newgroup.replace(/top\s*:[^"]*?;/i,"");
					var stylegp = newgroup.match(/style=["'].*?["']/i);
					if(!stylegp){//当无style样式时，追加style
						//newgroup = newgroup.replace(/>$/," style=\"position:absolute;left:"+left+";top:"+top+";\">");
						newgroup = newgroup.replace(/>$/," style=\"left:"+left+";top:"+top+";\">");
						//alert();
					}else{//当有style样式时，追加style的postion属性
						//var newstyle = stylegp[0].replace(/"$/,"position:absolute;left:"+left+";top:"+top+";\"");
						var newstyle = stylegp[0].replace(/"$/,"left:"+left+";top:"+top+";\"");
						newgroup = newgroup.replace(stylegp,newstyle);
					}
				}
				return str.replace(groups[0],newgroup);
			}
			
			// 更新元素绝对位置Style信息例position:absolute;left:99px;top:100px;
			function upSizeCode(sid,width,height,str){
				var regex3 = new RegExp("<div [^>]*sid=\""+sid+"\"[^>]*>","gi");	
				var groups = str.match(regex3);
				var newgroup = groups[0];
				if(groups){
					newgroup = newgroup.replace(/width\s*:[^"]*?;/i,"");
					newgroup = newgroup.replace(/height\s*:[^"]*?;/i,"");
					var stylegp = newgroup.match(/style=["'][^"]*?["']/i);
					if(!stylegp){//当无style样式时，追加style
						newgroup = newgroup.replace(/>$/," style=\"width:"+width+";height:"+height+";\">");
						//alert();
					}else{//当有style样式时，追加style的sizes属性
						var newstyle = stylegp[0].replace(/"$/,"width:"+width+";height:"+height+";\"");
						newgroup = newgroup.replace(stylegp,newstyle);
					}
				}
				return str.replace(groups[0],newgroup);
			}
		</script>

		<!-- 3、编辑缓存器 -->
		<script>
			var editorCache = []; // 页面缓存数组用于CTRLZ回退
			function editorCachePush(){
				
				/*
				if(editorCache.length==0){
					editorCache.push(codeEditor.getValue());
				}else if(editorCache.length>0){				
					if(codeEditor.getValue() != editorCache[editorCache.length-1]){
						editorCache.push(codeEditor.getValue());
					}else{
					}
				}
				*/
			}
			function editorCachePop(){
				/*
				var last_text = editorCache.pop();
				if(!last_text){return;}
				var main = getCode('main',last_text,'div');
				var mainInner = (main = main.replace(/<div [^>]*sid="main"[^>]*>/i,"")).substring(0,main.search(/<\/div>$/gi));
				$('[sid=main]').html(mainInner);
				//alert($('[sid=main]')[0]);
				$.parser.parse($('[sid=main]')[0]);
				codeEditor.setValue(last_text);
				$('#EditDiv [sid][sid!=main]').draggable({disabled:false,onBeforeDrag:onBeforeDrag,onStopDrag:onStopDrag});
				$('#EditDiv [sid][sid!=main]').resizable({onStartResize:onStartResize,onStopResize:onStopResize});
				*/
			}
		</script>

		<!-- 4、硬盘保存器 -->
		<script>
			// 将设计后的源代码保存
			function saveAsLocalHtml(){
			  try{
				var fso = new ActiveXObject("Scripting.FileSystemObject");
				var bpath = window.location.href.replace("file:///","").replace("#","");
				bpath = bpath.substring(0,(bpath.lastIndexOf("/")==-1?bpath.lastIndexOf("\\"):bpath.lastIndexOf("/"))+1);
				// alert(bpath);
				var f = fso.CreateTextFile("design.html", true,true); 
				f.write(codeEditor.getValue());
				f.Close();
				alert('已保存至'+fso.GetAbsolutePathName("design.html"));
			   }catch(e){alert('保存到本地失败!');}
			}
		</script>

	
</body>
</html>